package com.mdp.swagger;

import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import io.swagger.annotations.ApiModelProperty;
import javassist.*;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.annotation.MemberValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.OperationBuilderPlugin;
import springfox.documentation.spi.service.contexts.OperationContext;
import springfox.documentation.spring.web.DescriptionResolver;
import springfox.documentation.swagger.common.SwaggerPluginSupport;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * ApiJsonParameterBuilder
 * <p>
 * 将map入参匹配到swagger文档的工具类
 * plugin加载顺序，默认是最后加载
 *
 */
@Component
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1)
@ConditionalOnBean(DescriptionResolver.class)
public class ApiEntityParamsBuilder implements OperationBuilderPlugin {
    private static final Logger logger = LoggerFactory.getLogger(ApiEntityParamsBuilder.class);

    private final DescriptionResolver descriptions;

    @Autowired
    public ApiEntityParamsBuilder(DescriptionResolver descriptions) {
        this.descriptions = descriptions;
    }

    @Autowired
    private TypeResolver typeResolver;
    /**
     * 动态生成的Class的包路径 自由定义
     */
    private final static String BASE_PACKAGE = "com.mdp.entity.";
    /**
     * 默认类名
     */
    private final static String DEFAULT_CLASS_NAME = "RequestBody";
    /**
     * 序号 防止重名
     */
    private static Integer i = 0;


    public void apply(OperationContext context) {
        context.operationBuilder().parameters(this.readParameters(context)).build();
    }
    public boolean supports(DocumentationType delimiter) {
        return SwaggerPluginSupport.pluginDoesApply(delimiter);
    }

    private List<Parameter> readParameters(OperationContext context) {
        Optional<ApiEntityParams> annotation = context.findAnnotation(ApiEntityParams.class);
        List<Parameter> parameters = Lists.newArrayList();
        if (annotation.isPresent()) {
            ApiEntityParams apiAnno = annotation.get();
            if("query".equals(apiAnno.paramType())){
                return readParametersByModel(apiAnno);
            }else {
                try {
                    Parameter parameter=readParameterByModel(context,apiAnno);
                    parameters.add(parameter);
                } catch (NotFoundException e) {
                    e.printStackTrace();
                } catch (CannotCompileException e) {
                    e.printStackTrace();
                }

                return parameters;
            }

        }

        return parameters;
    }
    public Parameter readParameterByModel(OperationContext context,ApiEntityParams apiAnno) throws NotFoundException, CannotCompileException {
        String key = DEFAULT_CLASS_NAME + i++;
        try {
            //类名重复将导致swagger识别不准确 主动触发异常
            Class.forName(BASE_PACKAGE + key);
        } catch (ClassNotFoundException e) {
                String[] fields = apiAnno.props();

                    ClassPool pool = ClassPool.getDefault();
                    pool.appendClassPath(new LoaderClassPath(Thread.currentThread().getContextClassLoader()));
                    CtClass ctClass = pool.makeClass(BASE_PACKAGE + key);
                    ctClass.setModifiers(Modifier.ABSTRACT);
                    //处理 javassist.NotFoundException
                    pool.insertClassPath(new ClassClassPath(apiAnno.value()));
                    CtClass globalCtClass = pool.getCtClass(apiAnno.value().getName());
                    //从globalCtClass拷贝指定字段到动态创建的类中
                    if(fields!=null && fields.length>0) {
                        for (String field : merge(fields, ",")) {
                            //若指定的字段不存在 throw NotFoundException
                            CtField ctField = globalCtClass.getDeclaredField(field);
                            CtField newCtField = new CtField(ctField, ctClass);
                            handleField2(newCtField);
                            ctClass.addField(newCtField);

                        }
                    }else{
                        for (CtField ctField : globalCtClass.getDeclaredFields()) {
                            //若指定的字段不存在 throw NotFoundException
                            CtField newCtField = new CtField(ctField, ctClass);
                            handleField2(newCtField);
                            ctClass.addField(newCtField);

                        }
                    }
                    // 将生成的Class添加到SwaggerModels
                    context.getDocumentationContext().getAdditionalModels()
                            .add(typeResolver.resolve(ctClass.toClass()));


        }
        ParameterBuilder builder= new ParameterBuilder().parameterType("body").name("body");
        builder.modelRef(new ModelRef(key)).description(descriptions.resolve(apiAnno.remark())).required(false);
        return builder.build();
    }


    private void handleField2(CtField field) {
        //防止private又没有getter
        field.setModifiers(Modifier.PUBLIC);
        //有name的把字段名改为name
        //因为JSON格式化的原因,ApiModelProperty的name属性无效 所以如果有name,直接更改字段名为name
        AnnotationsAttribute annos = ((AnnotationsAttribute) field.getFieldInfo().getAttribute("RuntimeVisibleAnnotations"));
        if (annos != null) {
            Annotation anno = annos.getAnnotation(ApiModelProperty.class.getTypeName());
            if (anno != null) {
                MemberValue name = anno.getMemberValue("name");
                if (name != null) {
                    //这里返回的name会以引号包裹
                    String fName = name.toString().replace("\"", "").trim();
                    if (fName.length() > 0) {
                        field.setName(fName);
                    }
                }
            }
        }

    }
    public List<Parameter> readParametersByModel(ApiEntityParams apiAnno) {
        List<Parameter> parameters = Lists.newArrayList();
        try {
                String key = DEFAULT_CLASS_NAME + i++;
                try {
                    //类名重复将导致swagger识别不准确 主动触发异常
                    Class.forName(BASE_PACKAGE + key);
                } catch (ClassNotFoundException e) {
                    String[] fields = apiAnno.props();
                    ClassPool pool = ClassPool.getDefault();
                    CtClass ctClass = pool.makeClass(BASE_PACKAGE + key);
                    ctClass.setModifiers(Modifier.ABSTRACT);
                    //处理 javassist.NotFoundException
                    pool.insertClassPath(new ClassClassPath(apiAnno.value()));
                    CtClass globalCtClass = pool.getCtClass(apiAnno.value().getName());
                    CtField[] ctFields = globalCtClass.getDeclaredFields();

                    //从globalCtClass拷贝指定字段到动态创建的类中
                    if(fields!=null && fields.length>0){
                        for (String field : merge(fields, ",")) {
                            //若指定的字段不存在 throw NotFoundException
                            CtField ctField = globalCtClass.getDeclaredField(field);
                            CtField newCtField = new CtField(ctField, ctClass);
                            parameters.add(handleField(newCtField));
                            ctClass.addField(newCtField);
                        }
                    }else {
                        for (CtField field : ctFields) {
                            CtField newCtField = new CtField(field, ctClass);
                            parameters.add(handleField(newCtField));
                            ctClass.addField(newCtField);
                        }
                    }

            }
        } catch (Exception e) {
            logger.error("@ApiGlobalModel Error", e);
        }
        return parameters;
    }

    private Parameter handleField(CtField field) {
        ParameterBuilder builder= new ParameterBuilder().parameterType("query").name(field.getName());
        //防止private又没有getter
        field.setModifiers(Modifier.PUBLIC);
        String description="";
        //有name的把字段名改为name
        //因为JSON格式化的原因,ApiModelProperty的name属性无效 所以如果有name,直接更改字段名为name
        AnnotationsAttribute annos = ((AnnotationsAttribute) field.getFieldInfo().getAttribute("RuntimeVisibleAnnotations"));
        if (annos != null) {
            Annotation anno = annos.getAnnotation(ApiModelProperty.class.getTypeName());
            if (anno != null) {
                MemberValue notes=anno.getMemberValue("notes");
                if(notes!=null){
                    //这里返回的name会以引号包裹
                    description = notes.toString().replace("\"", "").trim();
                    if (description.length() > 0) {
                        builder.description(descriptions.resolve(description));
                    }
                }
            }
        }

        ModelRef modelRef = new ModelRef("string");
        builder.modelRef(modelRef).defaultValue("").required(false).allowMultiple(false);
        return builder.build();

    }

    /**
     * 字符串列表 分隔符 合并
     * A{"a","b,c","d"} => B{"a","d","b","c"}
     *
     * @param arr arr
     * @return list
     */
    private List<String> merge(String[] arr, String separator) {
        List<String> tmp = new ArrayList<>();
        Arrays.stream(arr).forEach(s -> {
            if (s.contains(separator)) {
                tmp.addAll(Arrays.asList(s.split(separator)));
            } else {
                tmp.add(s);
            }
        });
        return tmp;
    }

}